package com.meeruu.sg.account.core.domain.biz;

import com.meeruu.boot.did.service.MeeruuDidService;
import com.meeruu.common.utils.LoggerUtil;
import com.meeruu.common.utils.mapper.OrikaMapperUtils;
import com.meeruu.sg.account.core.api.constant.*;
import com.meeruu.sg.account.core.api.exception.AccountException;
import com.meeruu.sg.account.core.api.exception.ErrorCode;
import com.meeruu.sg.account.core.domain.mapper.AccountFrozenFlowMapper;
import com.meeruu.sg.account.core.domain.mapper.AccountFundsFlowMapper;
import com.meeruu.sg.account.core.domain.model.*;
import com.meeruu.sg.account.core.domain.service.MerchantAccountService;
import com.meeruu.sg.account.core.domain.service.OrgAccountService;
import com.meeruu.sg.account.core.domain.service.UserAccountService;
import com.meeruu.sg.account.core.domain.vo.account.*;
import com.meeruu.sg.account.core.domain.vo.enums.FrozenTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author yangqintao
 * @date 2019-05-17
 * @description 用户资金操作通用类
 */
@Slf4j
@Service
public class FundsBalanceBiz {

    @Autowired
    private UserAccountService userAccountService;

    @Autowired
    private MerchantAccountService merchantAccountService;

    @Autowired
    private OrgAccountService orgAccountService;

    @Resource
    private AccountFundsFlowMapper fundsFlowMapper;

    @Resource
    private AccountFrozenFlowMapper frozenFlowMapper;

    @Autowired
    private MeeruuDidService meeruuDidService;

    // 按字典顺序获取账户信息（悲观锁）
    public Map<String, BaseAccount> getAccountBySortForUpdate(AccountSortVo... accountSortVos) {
        List<AccountSortVo> accountIdList = Arrays.asList(accountSortVos);
        Collections.sort(accountIdList);

        Map<String, BaseAccount> result = new HashMap<>();
        accountIdList.forEach(
                accountMsg -> {
                    BaseAccount account = this.getAccount(accountMsg.getAccountId(), accountMsg.getAccountGroup());
                    result.put(accountMsg.getAccountId(), account);
                }

        );

        return result;
    }

    /**
     * 获取账户（悲观锁）
     */
    private BaseAccount getAccount(String accountId, AccountGroup accountGroup) {
        BaseAccount account = null;
        switch (accountGroup) {
            case USER:

                UserAccountDO userAccount = userAccountService.selectByAccountIdForUpdate(accountId);
                account = OrikaMapperUtils.map(userAccount, UserAccount.class);

                break;
            case MERCHANT:
                MerchantAccountDO merchantAccount = merchantAccountService.selectByAccountIdForUpdate(accountId);
                account = OrikaMapperUtils.map(merchantAccount, MerchantAccount.class);

                break;
            case ORG:
                OrgAccountDO orgAccount = orgAccountService.selectByAccountIdForUpdate(accountId);
                account = OrikaMapperUtils.map(orgAccount, OrgAccount.class);
                break;
        }

        return account;

    }

    /**
     * 用户账户更新（乐观锁）
     */
    public void updateBalance(BaseAccount account, AccountGroup accountGroup, BigDecimal beginBalance) {

        // 金额变动
        switch (accountGroup) {
            case USER:

                UserAccount userAccount = (UserAccount) account;
                UserAccountDO userAccountDO = OrikaMapperUtils.map(userAccount, UserAccountDO.class);
                userAccountService.updateAmount(
                        userAccountDO.getAvailableBalance(), userAccountDO.getFrozenBalance(),
                        userAccountDO.getTotalBalance(),
                        account.getAccountId(), beginBalance);
                break;
            case MERCHANT:

                MerchantAccount merchantAccount = (MerchantAccount) account;
                MerchantAccountDO merchantAccountDO = OrikaMapperUtils.map(merchantAccount, MerchantAccountDO.class);
                merchantAccountService.updateAmount(
                        merchantAccountDO.getAvailableBalance(), merchantAccountDO.getFrozenBalance(),
                        merchantAccountDO.getTotalBalance(),
                        account.getAccountId(), beginBalance);
                break;
            case ORG:

                OrgAccount orgAccount = (OrgAccount) account;
                OrgAccountDO orgAccountDO = OrikaMapperUtils.map(orgAccount, OrgAccountDO.class);
                orgAccountService.updateAmount(
                        orgAccountDO.getAvailableBalance(), orgAccountDO.getFrozenBalance(),
                        orgAccountDO.getTotalBalance(),
                        account.getAccountId(), beginBalance);
                break;
        }
    }

    /**
     * 资金流水添加
     *
     * @param accountId    账户id
     * @param refAccountId 关联账户id
     * @param amount       金额
     * @param direct       流水方向（0:支出 1:收入）
     * @param fundsOprType 流水业务类型
     * @param applyDate    申请日期
     * @param accountType  账户类型
     * @return 流水号
     */
    public String addFundsFlow(String accountId, String refAccountId, BigDecimal amount, FundsFlowDirect direct, FundsOprType fundsOprType, Date applyDate, Integer accountType) {

        String flowId = AccountUniqueId.FUNDS_FLOW_ID_PRE + meeruuDidService.nextId();

        // 添加入的流水
        AccountFundsFlowDO fundsFlow = AccountFundsFlowDO.builder()
                .flowId(flowId)
                .accountId(accountId)
                .refAccountId(refAccountId)
                .flowDirect(direct.getCode())
                .flowType(fundsOprType.getCode())
                .flowAmount(amount)
                .flowReason(fundsOprType.getDesc())
                .applyDate(applyDate)
                .accountType(accountType)
                .build();
        fundsFlowMapper.insert(fundsFlow);
        return flowId;
    }


    /**
     * 冻结记录添加
     *
     * @param accountId    账户id
     * @param accountGroup 账户组别
     * @param frozenAmount 冻结/解冻金额
     * @param frozenType   类型（0:冻结 1：正向解冻 2:逆向解冻)
     * @param flowType     业务类型
     * @param flowReason   原因
     * @param applyDate    申请日期
     * @param accountType  账户类型
     * @return 冻结记录流水号
     */
    public String addFrozenFlow(String accountId, int accountGroup, BigDecimal frozenAmount,
                                int frozenType, int flowType, String flowReason,
                                Date applyDate, Integer accountType, String refFlowId, FrozenTypeEnum frozenTypeEnum) {
        String frozenFlowId = frozenTypeEnum.getPrefix() + meeruuDidService.nextId();
        AccountFrozenFlowDO frozenFlowDO = AccountFrozenFlowDO.builder()
                .flowId(frozenFlowId)
                .refFlowId(refFlowId)
                .accountId(accountId)
                .accountGroup(accountGroup)
                .flowFrozen(frozenAmount)
                .frozenType(frozenType)
                .flowType(flowType)
                .flowReason(flowReason)
                .applyDate(applyDate)
                .accountType(accountType)
                .build();
        frozenFlowMapper.insert(frozenFlowDO);
        return frozenFlowId;
    }


    /**
     * 金额变动时校验账户状态
     * 1. 冻结状态 只进不出
     * 2. 拉黑状态 不进不出
     */
    public void checkStatus(Integer status, FundsFlowDirect flowType) {
        switch (flowType) {
            case IN:
                if (status.equals(AccountStatus.BLACK.getCode())) {
                    LoggerUtil.error(log, "金额变动失败，账户为拉黑状态");
                    throw new AccountException(ErrorCode.BIZ_ACCOUNT_STATUS_ERROR);
                }

                break;
            case OUT:
                if (!status.equals(AccountStatus.NORMAL.getCode())) {
                    LoggerUtil.error(log, "金额变动失败，账户不为正常状态");
                    throw new AccountException(ErrorCode.BIZ_ACCOUNT_STATUS_ERROR);
                }
                break;
        }
    }

}
